/*
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.wookie.beans.jpa;

import java.io.InputStream;
import java.lang.reflect.Array;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Iterator;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.RollbackException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.sql.DataSource;

import org.apache.commons.configuration.Configuration;
import org.apache.openjpa.persistence.Extent;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory;
import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.openjpa.persistence.QueryResultCache;
import org.apache.openjpa.persistence.StoreCache;
import org.apache.wookie.beans.*;
import org.apache.wookie.beans.jpa.impl.*;
import org.apache.wookie.beans.util.IPersistenceManager;
import org.apache.wookie.beans.util.PersistenceCommitException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * JPAPersistenceManager - JPA IPersistenceManager implementation.
 * 
 * @author <a href="mailto:rwatler@apache.org">Randy Watler</a>
 * @version $Id$
 */
public class JPAPersistenceManager implements IPersistenceManager {

	private static final Logger logger = LoggerFactory
			.getLogger(JPAPersistenceManager.class);
	public static final String WIDGET_DATABASE_JNDI_DATASOURCE_NAME = "jdbc/widgetdb";
	public static final String WIDGET_DATABASE_JNDI_DATASOURCE_FULL_NAME = "java:comp/env/"
			+ WIDGET_DATABASE_JNDI_DATASOURCE_NAME;
	public static final String PERSISTENCE_MANAGER_CACHE_SIZE_PROPERTY_NAME = "widget.persistence.manager.cachesize";
	public static final String PERSISTENCE_MANAGER_DB_TYPE_PROPERTY_NAME = "widget.persistence.manager.dbtype";
	private static final Map<Class<?>, Class<?>> INTERFACE_TO_CLASS_MAP = new HashMap<Class<?>, Class<?>>();
	private static final Map<Class<? extends IBean>, Class<? extends IBean>> BEAN_INTERFACE_TO_CLASS_MAP = new HashMap<Class<? extends IBean>, Class<? extends IBean>>();
	private static final Map<Class<? extends IBean>, Class<?>> BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP = new HashMap<Class<? extends IBean>, Class<?>>();
	private static final Map<String, String> DB_TYPE_TO_JPA_DICTIONARY_MAP = new HashMap<String, String>();

	static {
		INTERFACE_TO_CLASS_MAP.put(IAccessRequest.class,
				AccessRequestImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IApiKey.class, ApiKeyImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IDescription.class, DescriptionImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IFeature.class, FeatureImpl.class);
		INTERFACE_TO_CLASS_MAP.put(ILicense.class, LicenseImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IName.class, NameImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IParam.class, ParamImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IParticipant.class, ParticipantImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IPreference.class, PreferenceImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IPreferenceDefault.class,
				PreferenceDefaultImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IServerFeature.class,
				ServerFeatureImpl.class);
		INTERFACE_TO_CLASS_MAP.put(ISharedData.class, SharedDataImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IStartFile.class, StartFileImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IToken.class, TokenImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IWhitelist.class, WhitelistImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IWidget.class, WidgetImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IWidgetDefault.class,
				WidgetDefaultImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IWidgetIcon.class, WidgetIconImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IWidgetInstance.class,
				WidgetInstanceImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IWidgetService.class,
				WidgetServiceImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IWidgetType.class, WidgetTypeImpl.class);
		//IWC
        INTERFACE_TO_CLASS_MAP.put(IPost.class, PostImpl.class);


		INTERFACE_TO_CLASS_MAP.put(ITags.class, TagsImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IApikeyWidget.class, ApikeyWidgetImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IStoreUser.class, StoreUserImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IComments.class, CommentsImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IRating.class, RatingImpl.class);
		INTERFACE_TO_CLASS_MAP.put(ITagsWidgets.class, TagsWidgetsImpl.class);
		INTERFACE_TO_CLASS_MAP.put(IAccessLog.class, AccessLogImpl.class);
		
		

		BEAN_INTERFACE_TO_CLASS_MAP.put(IAccessRequest.class,
				AccessRequestImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IApiKey.class, ApiKeyImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IParticipant.class,
				ParticipantImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IPreference.class, PreferenceImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IServerFeature.class,
				ServerFeatureImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP
				.put(ISharedData.class, SharedDataImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IWhitelist.class, WhitelistImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IWidget.class, WidgetImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IWidgetDefault.class,
				WidgetDefaultImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IWidgetInstance.class,
				WidgetInstanceImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IWidgetService.class,
				WidgetServiceImpl.class);	
		BEAN_INTERFACE_TO_CLASS_MAP.put(IComments.class,
				CommentsImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IApikeyWidget.class,
				ApikeyWidgetImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IStoreUser.class, StoreUserImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IRating.class, RatingImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(ITagsWidgets.class, TagsWidgetsImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(ITags.class, TagsImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IAccessLog.class, AccessLogImpl.class);
		BEAN_INTERFACE_TO_CLASS_MAP.put(IApikeyWidget.class, ApikeyWidgetImpl.class);
		//IWC
        BEAN_INTERFACE_TO_CLASS_MAP.put(IPost.class, PostImpl.class);
		
		
		
		
		

		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IAccessRequest.class,
				Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IApiKey.class, Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IParticipant.class,
				Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IServerFeature.class,
				Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP
				.put(IWhitelist.class, Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IWidget.class, Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IWidgetDefault.class,
				String.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IWidgetInstance.class,
				Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IWidgetService.class,
				Integer.class);
		
		
		
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IApikeyWidget.class,
				Integer.class);		
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IRating.class,
				Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IStoreUser.class,
				Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(ITagsWidgets.class,
				Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(ITags.class,
				Integer.class);
		BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IAccessLog.class,
				Integer.class);
		
		//IWC
        BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP.put(IPost.class, Integer.class);


		// DB_TYPE_TO_JPA_DICTIONARY_MAP.put("db2", "db2");
		// DB_TYPE_TO_JPA_DICTIONARY_MAP.put("derby", "derby");
		// DB_TYPE_TO_JPA_DICTIONARY_MAP.put("hsqldb", "hsql");
		// DB_TYPE_TO_JPA_DICTIONARY_MAP.put("mssql", "sqlserver");
		DB_TYPE_TO_JPA_DICTIONARY_MAP.put("mysql", "mysql");
		// DB_TYPE_TO_JPA_DICTIONARY_MAP.put("mysql5", "mysql");
		// DB_TYPE_TO_JPA_DICTIONARY_MAP.put("oracle", "oracle");
		// DB_TYPE_TO_JPA_DICTIONARY_MAP.put("oracle9", "oracle");
		// DB_TYPE_TO_JPA_DICTIONARY_MAP.put("oracle10", "oracle");
		// DB_TYPE_TO_JPA_DICTIONARY_MAP.put("postgresql", "postgres");
		// DB_TYPE_TO_JPA_DICTIONARY_MAP.put("sybase", "sybase");
	}
	private static String cacheSize;
	private static String dbType;
	private static String dictionaryType;
	private static OpenJPAEntityManagerFactory entityManagerFactory;

	/**
	 * Initialize implementation with configuration.
	 * 
	 * @param configuration
	 *            configuration properties
	 * @param initializeStore
	 *            truncate and initialize persistent store
	 */
	public static void initialize(Configuration configuration,
			boolean initializeStore) {
		try {
			// configuration
			cacheSize = configuration
					.getString(PERSISTENCE_MANAGER_CACHE_SIZE_PROPERTY_NAME);
			dbType = configuration
					.getString(PERSISTENCE_MANAGER_DB_TYPE_PROPERTY_NAME);
			dictionaryType = ((dbType != null) ? DB_TYPE_TO_JPA_DICTIONARY_MAP
					.get(dbType) : null);
			if ((dbType != null) && (dictionaryType == null)) {
				throw new IllegalArgumentException(
						"Unsupported database type: " + dbType);
			}

			// initialize persistent store
			if (initializeStore && (dbType != null)) {
				// get datasource connection
				Context initialContext = new InitialContext();
				DataSource dataSource = (DataSource) initialContext
						.lookup(WIDGET_DATABASE_JNDI_DATASOURCE_FULL_NAME);
				Connection connection = dataSource.getConnection();
				connection.setAutoCommit(true);

				// execute initialization scripts
				String sqlScriptResource = dbType + "-wookie-schema.sql";
				InputStream sqlStream = JPAPersistenceManager.class
						.getResourceAsStream(sqlScriptResource);
				if (sqlStream == null) {
					throw new IllegalArgumentException(
							"Unsupported persistent store initialization script: "
									+ sqlScriptResource);
				}
				SQLScriptReader reader = new SQLScriptReader(sqlStream);
				int statementCount = 0;
				int statementErrorCount = 0;
				for (;;) {
					String scriptStatement = reader.readSQLStatement();
					if (scriptStatement != null) {
						Statement statement = connection.createStatement();
						statementCount++;
						try {
							statement.execute(scriptStatement);
						} catch (SQLException sqle) {
							statementErrorCount++;
						}
					} else {
						break;
					}
				}
				logger.info("Persistent store initialized from "
						+ sqlScriptResource + ", (" + statementCount
						+ " statements, " + statementErrorCount + " errors)");

				// close datasource connection
				connection.close();
			}

			// initialize entity manager factory
			Properties persistenceProperties = new Properties();
			InputStream propertiesStream = JPAPersistenceManager.class
					.getResourceAsStream("persistence.properties");
			if (propertiesStream == null) {
				throw new IllegalArgumentException(
						"Unable to load configuration: persistence.properties");
			}
			persistenceProperties.load(propertiesStream);
			if (cacheSize != null) {
				int dataCacheSize = Integer.parseInt(cacheSize);
				persistenceProperties.setProperty("openjpa.DataCache",
						"true(CacheSize=" + dataCacheSize
								+ ",SoftReferenceSize=0)");
				int queryCacheSize = Integer.parseInt(cacheSize) / 10;
				persistenceProperties.setProperty("openjpa.QueryCache",
						"CacheSize=" + queryCacheSize + ",SoftReferenceSize=0");
			}
			if (dictionaryType != null) {
				persistenceProperties.setProperty("openjpa.jdbc.DBDictionary",
						dictionaryType);
			}
			EntityManagerFactory factory = Persistence
					.createEntityManagerFactory("wookie", persistenceProperties);
			entityManagerFactory = OpenJPAPersistence.cast(factory);

			logger.info("Initialized");
		} catch (Exception e) {
			throw new RuntimeException("Unable to initialize 1: " + e, e);
		}
	}

	/**
	 * Terminate implementation.
	 */
	public static void terminate() {
		try {
			// evict all caches
			StoreCache cache = entityManagerFactory.getStoreCache();
			cache.evictAll();
			QueryResultCache queryCache = entityManagerFactory
					.getQueryResultCache();
			queryCache.evictAll();

			// close entity manager factory
			entityManagerFactory.close();
			entityManagerFactory = null;

			logger.info("Terminated");
		} catch (Exception e) {
			throw new RuntimeException("Unable to terminate: " + e, e);
		}
	}

	private EntityManager entityManager;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.wookie.beans.util.IPersistenceManager#begin()
	 */
	public void begin() {
		// validate entity manager transaction
		if (entityManager != null) {
			throw new IllegalStateException("Transaction already initiated");
		}

		// create entity manager and start transaction
		entityManager = entityManagerFactory.createEntityManager();
		EntityTransaction transaction = entityManager.getTransaction();
		if (!transaction.isActive()) {
			transaction.begin();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.wookie.beans.util.IPersistenceManager#close()
	 */
	public void close() {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// rollback transaction and close entity manager
		EntityTransaction transaction = entityManager.getTransaction();
		if (transaction.isActive()) {
			transaction.rollback();
		}
		entityManager.clear();
		entityManager.close();
		entityManager = null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.wookie.beans.util.IPersistenceManager#commit()
	 */
	public void commit() throws PersistenceCommitException {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// commit transaction
		EntityTransaction transaction = entityManager.getTransaction();
		if (transaction.isActive()) {
			try {
				transaction.commit();
			} catch (RollbackException re) {
				throw new PersistenceCommitException(
						"Transaction commit exception: " + re, re);
			} catch (OptimisticLockException ole) {
				throw new PersistenceCommitException(
						"Transaction locking/version commit exception: " + ole,
						ole);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#delete(org.apache.wookie
	 * .beans.IBean)
	 */
	public boolean delete(IBean bean) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// remove bean
		try {
			entityManager.remove(bean);
			return true;
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
			return false;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#delete(org.apache.wookie
	 * .beans.IBean[])
	 */
	public boolean delete(IBean[] beans) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// remove beans
		try {
			for (IBean bean : beans) {
				entityManager.remove(bean);
			}
			return true;
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
			return false;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findAll(java.lang.Class)
	 */
	@SuppressWarnings("unchecked")
	public <T extends IBean> T[] findAll(Class<T> beansInterface) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// validate bean interface
		Class<? extends IBean> beanClass = BEAN_INTERFACE_TO_CLASS_MAP
				.get(beansInterface);
		if (beanClass == null) {
			throw new IllegalArgumentException(
					"Invalid bean interface specified");
		}

		// get persistent bean extent
		try {
			OpenJPAEntityManager manager = OpenJPAPersistence
					.cast(entityManager);
			Extent<? extends IBean> beansExtent = manager.createExtent(
					beanClass, true);
			List<? extends IBean> beansExtentList = beansExtent.list();
			if ((beansExtentList != null) && !beansExtentList.isEmpty()) {
				return beansExtentList.toArray((T[]) Array.newInstance(
						beansInterface, beansExtentList.size()));
			}
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
		}
		return (T[]) Array.newInstance(beansInterface, 0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findApplicableAccessRequests
	 * (org.apache.wookie.beans.IWidget)
	 */
	@SuppressWarnings("unchecked")
	public IAccessRequest[] findApplicableAccessRequests(IWidget widget) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get applicable access requests for widget using custom query
		if (widget != null) {
			try {
				Query query = entityManager.createNamedQuery("ACCESS_REQUESTS");
				query.setParameter("widget", widget);
				List<IParticipant> accessRequestsList = query.getResultList();
				if ((accessRequestsList != null)
						&& !accessRequestsList.isEmpty()) {
					return accessRequestsList
							.toArray(new IAccessRequest[accessRequestsList
									.size()]);
				}
			} catch (Exception e) {
				logger.error("Unexpected exception: " + e, e);
			}
		}
		return new IAccessRequest[0];

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findById(java.lang.Class
	 * , java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public <T extends IBean> T findById(Class<T> beanInterface, Object id) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// validate bean interface
		Class<? extends IBean> beanClass = BEAN_INTERFACE_TO_CLASS_MAP
				.get(beanInterface);
		Class<?> idFieldType = BEAN_INTERFACE_TO_ID_FIELD_TYPE_MAP
				.get(beanInterface);
		if ((beanClass == null) || (idFieldType == null)) {
			throw new IllegalArgumentException(
					"Invalid bean interface specified");
		}

		// get persistent bean by primary key
		try {
			if (id != null) {
				if ((idFieldType == Integer.class) && !(id instanceof Integer)) {
					try {
						id = new Integer(id.toString());
					} catch (NumberFormatException nfe) {
						return null;
					}
				} else if ((idFieldType == String.class)
						&& !(id instanceof String)) {
					id = id.toString();
				}
			}
			return (T) entityManager.find(beanClass, id);
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findByValue(java.lang
	 * .Class, java.lang.String, java.lang.Object, java.lang.String, boolean)
	 */
	@SuppressWarnings("unchecked")
	public <T extends IBean> T[] findByValue(Class<T> beansInterface,
			String name, Object value, String orderBy, boolean ascending) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// validate bean interface
		Class<? extends IBean> beanClass = BEAN_INTERFACE_TO_CLASS_MAP
				.get(beansInterface);
		if (beanClass == null) {
			throw new IllegalArgumentException(
					"Invalid bean interface specified");
		}

		// get persistent beans by criteria
		try {
			// construct query criteria
			CriteriaBuilder criteriaBuilder = entityManager
					.getCriteriaBuilder();
			CriteriaQuery<? extends IBean> criteriaQuery = criteriaBuilder
					.createQuery(beanClass);
			Root<? extends IBean> beanRoot = criteriaQuery.from(beanClass);
			if (name != null) {
				criteriaQuery.where((value != null) ? criteriaBuilder.equal(
						beanRoot.get(name), value) : criteriaBuilder
						.isNull(beanRoot.get(name)));
			}
			if (orderBy != null) {
				criteriaQuery.orderBy(ascending ? criteriaBuilder.asc(beanRoot
						.get(orderBy)) : criteriaBuilder.desc(beanRoot
						.get(orderBy)));
			}

			// invoke query
			Query query = entityManager.createQuery(criteriaQuery);
			List<? extends IBean> beansList = query.getResultList();
			if ((beansList != null) && !beansList.isEmpty()) {
				return beansList.toArray((T[]) Array.newInstance(
						beansInterface, beansList.size()));
			}
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
		}
		return (T[]) Array.newInstance(beansInterface, 0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findByValue(java.lang
	 * .Class, java.lang.String, java.lang.Object)
	 */
	public <T extends IBean> T[] findByValue(Class<T> beansInterface,
			String name, Object value) {
		return findByValue(beansInterface, name, value, null, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findByValues(java.lang
	 * .Class, java.util.Map, java.lang.String, boolean)
	 */
	@SuppressWarnings("unchecked")
	public <T extends IBean> T[] findByValues(Class<T> beansInterface,
			Map<String, Object> values, String orderBy, boolean ascending) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// validate bean interface
		Class<? extends IBean> beanClass = BEAN_INTERFACE_TO_CLASS_MAP
				.get(beansInterface);
		if (beanClass == null) {
			throw new IllegalArgumentException(
					"Invalid bean interface specified");
		}

		// get persistent beans by criteria
		try {
			// construct query criteria
			CriteriaBuilder criteriaBuilder = entityManager
					.getCriteriaBuilder();
			CriteriaQuery<? extends IBean> criteriaQuery = criteriaBuilder
					.createQuery(beanClass);
			Root<? extends IBean> beanRoot = criteriaQuery.from(beanClass);
			if ((values != null) && !values.isEmpty()) {
				Predicate predicate = null;
				for (Map.Entry<String, Object> value : values.entrySet()) {
					Predicate valuePredicate = ((value.getValue() != null) ? criteriaBuilder
							.equal(beanRoot.get(value.getKey()),
									value.getValue()) : criteriaBuilder
							.isNull(beanRoot.get(value.getKey())));
					predicate = ((predicate != null) ? criteriaBuilder.and(
							predicate, valuePredicate) : valuePredicate);
				}
				criteriaQuery.where(predicate);
			}
			if (orderBy != null) {
				criteriaQuery.orderBy(ascending ? criteriaBuilder.asc(beanRoot
						.get(orderBy)) : criteriaBuilder.desc(beanRoot
						.get(orderBy)));
			}

			// invoke query
			Query query = entityManager.createQuery(criteriaQuery);
			List<? extends IBean> beansList = query.getResultList();
			if ((beansList != null) && !beansList.isEmpty()) {
				return beansList.toArray((T[]) Array.newInstance(
						beansInterface, beansList.size()));
			}
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
		}
		return (T[]) Array.newInstance(beansInterface, 0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findByValues(java.lang
	 * .Class, java.util.Map)
	 */
	public <T extends IBean> T[] findByValues(Class<T> beansInterface,
			Map<String, Object> values) {
		return findByValues(beansInterface, values, null, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findParticipants(org
	 * .apache.wookie.beans.IWidgetInstance)
	 */
	@SuppressWarnings("unchecked")
	public IParticipant[] findParticipants(IWidgetInstance widgetInstance) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get participants for widget instance using custom query
		if (widgetInstance != null) {
			try {
				String sharedDataKey = widgetInstance.getSharedDataKey();
				IWidget widget = widgetInstance.getWidget();
				Query query = entityManager.createNamedQuery("PARTICIPANTS");
				query.setParameter("sharedDataKey", sharedDataKey);
				query.setParameter("widget", widget);
				List<IParticipant> participantsList = query.getResultList();
				if ((participantsList != null) && !participantsList.isEmpty()) {
					return participantsList
							.toArray(new IParticipant[participantsList.size()]);
				}
			} catch (Exception e) {
				logger.error("Unexpected exception: " + e, e);
			}
		}
		return new IParticipant[0];
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findParticipantViewer
	 * (org.apache.wookie.beans.IWidgetInstance)
	 */
	public IParticipant findParticipantViewer(IWidgetInstance widgetInstance) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get participant viewer for widget instance using custom query
		if (widgetInstance != null) {
			try {
				String sharedDataKey = widgetInstance.getSharedDataKey();
				IWidget widget = widgetInstance.getWidget();
				String userId = widgetInstance.getUserId();
				Query query = entityManager
						.createNamedQuery("PARTICIPANT_VIEWER");
				query.setParameter("sharedDataKey", sharedDataKey);
				query.setParameter("widget", widget);
				query.setParameter("userId", userId);
				return (IParticipant) query.getSingleResult();
			} catch (NoResultException nre) {
			} catch (Exception e) {
				logger.error("Unexpected exception: " + e, e);
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findServerFeatureByName
	 * (java.lang.String)
	 */
	public IServerFeature findServerFeatureByName(String name) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get server feature by name using custom query
		if (name != null) {
			try {
				Query query = entityManager.createNamedQuery("SERVER_FEATURE");
				query.setParameter("featureName", name);
				return (IServerFeature) query.getSingleResult();
			} catch (NoResultException nre) {
			} catch (Exception e) {
				logger.error("Unexpected exception: " + e, e);
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findServerFeatureNames()
	 */
	@SuppressWarnings("unchecked")
	public String[] findServerFeatureNames() {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get server feature names using custom query
		try {
			Query query = entityManager
					.createNamedQuery("SERVER_FEATURE_NAMES");
			List<String> namesList = query.getResultList();
			if ((namesList != null) && !namesList.isEmpty()) {
				return namesList.toArray(new String[namesList.size()]);
			}
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
		}
		return new String[0];
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findWidgetByGuid(java
	 * .lang.String)
	 */
	public IWidget findWidgetByGuid(String guid) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get widget by GUID using custom query
		if (guid != null) {
			try {
				Query query = entityManager.createNamedQuery("WIDGET");
				query.setParameter("guid", guid);
				return (IWidget) query.getSingleResult();
			} catch (NoResultException nre) {
			} catch (Exception e) {
				logger.error("Unexpected exception: " + e, e);
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findWidgetDefaultByType
	 * (java.lang.String)
	 */
	public IWidget findWidgetDefaultByType(String widgetContext) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get default widget by type using custom query
		if (widgetContext != null) {
			try {
				Query query = entityManager.createNamedQuery("DEFAULT_WIDGET");
				query.setParameter("widgetContext", widgetContext);
				return (IWidget) query.getSingleResult();
			} catch (NoResultException nre) {
			} catch (Exception e) {
				logger.error("Unexpected exception: " + e, e);
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findWidgetInstance(java
	 * .lang.String, java.lang.String, java.lang.String, java.lang.String)
	 */
	public IWidgetInstance findWidgetInstance(String apiKey, String userId,
			String sharedDataKey, String serviceContext) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get widget instance using custom query
		if ((apiKey != null) && (userId != null) && (sharedDataKey != null)
				&& (serviceContext != null)) {
			try {
				Query query = entityManager.createNamedQuery("WIDGET_INSTANCE");
				query.setParameter("apiKey", apiKey);
				query.setParameter("userId", userId);
				query.setParameter("sharedDataKey", sharedDataKey);
				query.setParameter("widgetContext", serviceContext);
				return (IWidgetInstance) query.getSingleResult();
			} catch (NoResultException nre) {
			} catch (Exception e) {
				logger.error("Unexpected exception: " + e, e);
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findWidgetInstanceByGuid
	 * (java.lang.String, java.lang.String, java.lang.String, java.lang.String)
	 */
	public IWidgetInstance findWidgetInstanceByGuid(String apiKey,
			String userId, String sharedDataKey, String widgetGuid) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get widget instance using custom query
		if ((apiKey != null) && (userId != null) && (sharedDataKey != null)
				&& (widgetGuid != null)) {
			try {
				Query query = entityManager
						.createNamedQuery("WIDGET_INSTANCE_GUID");
				query.setParameter("apiKey", apiKey);
				query.setParameter("userId", userId);
				query.setParameter("sharedDataKey", sharedDataKey);
				query.setParameter("guid", widgetGuid);
				return (IWidgetInstance) query.getSingleResult();
			} catch (NoResultException nre) {
			} catch (Exception e) {
				logger.error("Unexpected exception: " + e, e);
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findWidgetInstanceByIdKey
	 * (java.lang.String)
	 */
	public IWidgetInstance findWidgetInstanceByIdKey(String idKey) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get widget instance using custom query
		if (idKey != null) {
			try {
				Query query = entityManager
						.createNamedQuery("WIDGET_INSTANCE_ID");
				query.setParameter("idKey", idKey);
				return (IWidgetInstance) query.getSingleResult();
			} catch (NoResultException nre) {
			} catch (Exception e) {
				logger.error("Unexpected exception: " + e, e);
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#findWidgetsByType(java
	 * .lang.String)
	 */
	@SuppressWarnings("unchecked")
	public IWidget[] findWidgetsByType(String widgetContext) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// get widgets by type using custom query
		if (widgetContext != null) {
			try {
				Query query = entityManager.createNamedQuery("WIDGETS");
				query.setParameter("widgetContext", widgetContext);
				List<IWidget> widgetsList = query.getResultList();
				if ((widgetsList != null) && !widgetsList.isEmpty()) {
					return widgetsList.toArray(new IWidget[widgetsList.size()]);
				}
			} catch (Exception e) {
				logger.error("Unexpected exception: " + e, e);
			}
		}
		return new IWidget[0];
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#newInstance(java.lang
	 * .Class)
	 */
	@SuppressWarnings("unchecked")
	public <T> T newInstance(Class<T> instanceInterface) {
		// validate instance interface
		Class<?> instanceClass = INTERFACE_TO_CLASS_MAP.get(instanceInterface);
		if (instanceClass == null) {
			throw new IllegalArgumentException(
					"Invalid instance interface specified");
		}

		// create new instance of persistent class
		try {
			return (T) instanceClass.newInstance();
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.wookie.beans.util.IPersistenceManager#rollback()
	 */
	public void rollback() {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// commit transaction
		EntityTransaction transaction = entityManager.getTransaction();
		if (transaction.isActive()) {
			transaction.rollback();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.wookie.beans.util.IPersistenceManager#save(org.apache.wookie
	 * .beans.IBean)
	 */
	public boolean save(IBean bean) {
		// validate entity manager transaction
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		// persist new or update existing bean
		try {
			entityManager.persist(bean);
			return true;
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
			return false;
		}
	}

	@Override
	public List<ITags> findLike(String tagText) {
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}
		try {
			// System.out.println("going to create a named query");
			// Query query =
			// entityManager.createNamedQuery("TagsImpl.findLike");
			// query.setParameter("tagtext", "'%"+tagText+"%'");
			// query.setParameter("tagtext", ""+tagText+"");
			// query.setParameter("esc","\\");
			Query query = entityManager
					.createQuery("SELECT t FROM TagsImpl t WHERE t.tagtext LIKE '%"
							+ tagText + "%'");
			List<ITags> tagsList = query.getResultList();

			// System.out.println("------");
			// System.out.println("Tags List");
			// Iterator it = tagsList.iterator();
			// while (it.hasNext()) {
			// Object o = it.next();
			// if (o instanceof TagsImpl) {
			// TagsImpl t = (TagsImpl) o;
			// System.out.println("t=" + t.getTagtext());
			// }
			// }

			if ((tagsList != null) && !tagsList.isEmpty()) {
				return tagsList;// .toArray(new ITags[tagsList.size()]);
			}
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
		}
		return null;
	}

	@Override
	public List<IDescription> findWidgetsLike(String keyword) {
		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}
		try {
			Query query = entityManager
					.createQuery("SELECT wi FROM DescriptionImpl wi WHERE wi.content LIKE '%"
							+ keyword + "%'");
			List<IDescription> matchList = query.getResultList();
			
			if ((matchList != null) && !matchList.isEmpty()) {
				return matchList;// .toArray(new ITags[tagsList.size()]);
			}
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
		}
		return null;
	}
	
	public IStoreUser getStoreUser(String userName) {

		if (entityManager == null) {
			throw new IllegalStateException(
					"Transaction not initiated or already closed");
		}

		IStoreUser isu = null;
		try {
			Query query = entityManager.createNamedQuery("StoreUserImpl.findByUsername");
			query.setParameter("username", userName);
			isu = (IStoreUser) query.getSingleResult();
			return isu;
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
		}
		return isu;
	}
	
	public EntityManager getEntityManager(){
		return entityManager;
	}
	
	public <T> List<T> JPQLQuery(Class<T> bean , String queryString){
		try {
			Query query = entityManager.createQuery(queryString);			
			List<T> result = query.getResultList();
			return result;
		} catch (Exception e) {
			logger.error("Unexpected exception: " + e, e);
		}
		return null;
	}
}
